/**
 * 
 */
package edu.cmu.mism.dgjava.algorithm.impl;

import org.eclipse.core.runtime.IProgressMonitor;

import edu.cmu.mism.dgjava.data.models.option.BinomialAmerican;
import edu.cmu.mism.dgjava.data.models.option.Equity;
import edu.cmu.mism.dgjava.data.models.option.OptionCalculationResult;
import edu.cmu.mism.dgjava.data.models.option.impl.OptionModelFactoryImpl;

/**
 * @author Christian
 * 
 */
public class BinomialAmericanAlg extends BinomialAlg {

	

	public BinomialAmericanAlg(Object underlyingModel, Object pricingModel) {
		super(underlyingModel, pricingModel);

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see edu.cmu.mism.dgjava.algorithm.IOptionAlgorithm#calculate()
	 */
	@Override
	public OptionCalculationResult calculate(IProgressMonitor monitor) {

		OptionCalculationResult result = OptionModelFactoryImpl.eINSTANCE
		.createOptionCalculationResult();
		
		initializeOptionsVariable();
		calculateOptionsParameters();
		calculateOption();
		copyDuplicateTreeNode();
		// Needed because for the calculation of theta and vega, the treeNode value changes.
		actualOptionPrice = treeNode[0][0].optionPrice;
		calculateGreekLetters();
		result.setPrice(actualOptionPrice);
		result.setDelta(delta);
		result.setGamma(gamma);		
		result.setRho(rho);
		result.setTheta(theta);
		result.setVega(vega);
		result.setTree(treeNode);

		return result;
	}

	/**
	 * Calculate the Greek letter Vega
	 */
	protected void calculateVega() {
		
		// Initialize the options variable again as entered by the users
		initializeOptionsVariable();
		// Increment the value of volatility by 1% and calculate the new option prices by this change. 
		volatility = volatility + 0.01;
		// Re-calculate the option parameters again to consider the change to volatility
		calculateOptionsParameters();
		// Calculate the option price again due to change in volatility 
		calculateOption();
		vega = (treeNode[0][0].optionPrice - actualOptionPrice)/(0.01 * 100);
		
	}
	
	/**
	 * Calculate the Greek Letter Rho 
	 */
	protected void calculateRho() {
		
		// Initialize the options variable again as entered by the users
		initializeOptionsVariable();
		// Increment the value of riskFreeRate by 1% and calculate the new option prices by this change.
		riskFreeRate = riskFreeRate + 0.01;
		// Re-calculate the option parameters again to consider the change to volatility
		calculateOptionsParameters();
		// Calculate the option price again due to change in volatility
		calculateOption();
		rho = (treeNode[0][0].optionPrice - actualOptionPrice)/(0.01 * 100);
						
	}
	

	/**
	 * Using Binomial method, calculate the value of option prices at various intervals of time.
	 * The higher the number of tree steps, the more accurate the result is.
	 */
	protected void calculateOption() {
		
		treeNode = new TreeNode[numberOfTreeSteps + 1][];
		for (int i = numberOfTreeSteps; i >= 0; i--) {

			treeNode[i] = new TreeNode[numberOfTreeSteps + 1];

			for (int j = 0; j <= i; j++) {

				treeNode[i][j] = new TreeNode();
				treeNode[i][j].stockPrice = stockPrice
						* Math.pow(upFactor, 2 * j - i);

				if (i == numberOfTreeSteps) {

					treeNode[numberOfTreeSteps][j].optionPrice = calculateOptionPrice(treeNode[numberOfTreeSteps][j].stockPrice, strikePrice, 0.0);
					if(treeNode[numberOfTreeSteps][j].optionPrice > 0){
						treeNode[numberOfTreeSteps][j].canExercise = true;
					}
				} else {
					
					double optionValue = Math.pow(Math.E, -riskFreeRate
							* deltaTime)
							* (probabilityOfUp
									* treeNode[i + 1][j + 1].optionPrice + (1 - probabilityOfUp)
									* treeNode[i + 1][j].optionPrice);
					
					treeNode[i][j].optionPrice = calculateOptionPrice(treeNode[i][j].stockPrice, strikePrice, optionValue);
					
					if(treeNode[i][j].optionPrice > optionValue){
						treeNode[i][j].canExercise = true;
						
					}
					
				}
				
			}
		}

	}


	/**
	 * Initialize the variables entered by the user
	 */
	private void initializeOptionsVariable() {
		
		stockPrice = ((Equity)_underlyingModel).getStockPrice();
		riskFreeRate = ((Equity)_underlyingModel).getRiskFreeRate()/100.0;
		volatility = ((Equity)_underlyingModel).getVolatility()/100.0;

		strikePrice = ((BinomialAmerican)_pricingModel).getExercisePrice();
		numberOfTreeSteps = ((BinomialAmerican)_pricingModel).getTreeSteps();
		timeToExpiration = ((BinomialAmerican)_pricingModel).getTimeToExpire();

		type = ((BinomialAmerican)_pricingModel).getType();

	}

}
